package boardgamer.common.client.animation;

import android.graphics.Canvas;
import android.util.Log;
import android.util.Pair;
import boardgamer.common.client.Logger;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;

import java.util.Collection;
import java.util.concurrent.ConcurrentLinkedQueue;

import static com.google.common.base.Preconditions.checkNotNull;

public class AnimationCollection {

  public static final DeactivateHandler NOOP = new DeactivateHandler() {
    @Override
    public void onDeactivate() {
      // noop
    }
  };

  @VisibleForTesting
  final ConcurrentLinkedQueue<
      Pair<BlockingAnimation, ? extends DeactivateHandler>> blockingAnimations;
  @VisibleForTesting
  final ConcurrentLinkedQueue<Pair<Animation, ? extends DeactivateHandler>> nonBlockingAnimations;

  public AnimationCollection() {
    this.blockingAnimations
        = new ConcurrentLinkedQueue<Pair<BlockingAnimation, ? extends DeactivateHandler>>();
    this.nonBlockingAnimations
        = new ConcurrentLinkedQueue<Pair<Animation, ? extends DeactivateHandler>>();
  }

  public void update(int elapsedTime) {
    synchronized (blockingAnimations) {
      if (!blockingAnimations.isEmpty()) {
        Pair<BlockingAnimation, ? extends DeactivateHandler> pair = blockingAnimations.peek();
        BlockingAnimation animation = pair.first;
        animation.update(elapsedTime);
        if (!animation.isBlocking()) {
          blockingAnimations.remove();
          pair.second.onDeactivate();
        }
      }
    }

    synchronized (nonBlockingAnimations) {
      Collection<Pair<Animation, ? extends DeactivateHandler>> toRemove = Sets.newHashSet();
      for (Pair<Animation, ? extends DeactivateHandler> pair : nonBlockingAnimations) {
        Animation animation = pair.first;
        animation.update(elapsedTime);
        if (animation.isDead()) {
          pair.second.onDeactivate();
          toRemove.add(pair);
        }
      }
      nonBlockingAnimations.removeAll(toRemove);
    }
  }

  public void draw(Canvas canvas) {
    synchronized (blockingAnimations) {
      if (!blockingAnimations.isEmpty()) {
        blockingAnimations.peek().first.draw(canvas);
      }
    }

    synchronized (nonBlockingAnimations) {
      for (Pair<Animation, ? extends DeactivateHandler> pair : nonBlockingAnimations) {
        pair.first.draw(canvas);
      }
    }
  }

  public boolean isBlocking() {
    boolean returnValue;
    Logger.log(Log.ERROR, "### isBlocking?");
    synchronized (blockingAnimations) {
      returnValue = !blockingAnimations.isEmpty();
    }

    Logger.log(Log.ERROR, "### isBlocking? " + returnValue);
    return returnValue;
  }

  public AnimationCollection addBlocking(BlockingAnimation animation) {
    return addBlocking(animation, NOOP);
  }

  /**
   * Adds a blocking boardgamer.yavalath.client.animation to this collection. If there is an boardgamer.yavalath.client.animation that has been added
   * previously, the new boardgamer.yavalath.client.animation will not be updated or drawn until the previous one is no
   * longer blocking.
   * <p/>
   * The specified {@link DeactivateHandler} is called when the boardgamer.yavalath.client.animation is unblocked
   */
  public AnimationCollection addBlocking(BlockingAnimation animation, DeactivateHandler handler) {
    synchronized (blockingAnimations) {
      blockingAnimations.add(Pair.create(animation, checkNotNull(handler, "handler")));
    }
    return this;
  }

  public AnimationCollection addTemporary(Animation animation) {
    return addTemporary(animation, NOOP);
  }
  
  public AnimationCollection addTemporary(Animation animation, DeactivateHandler handler) {
    synchronized (nonBlockingAnimations) {
      nonBlockingAnimations.add(Pair.create(animation, handler));
    }
    return this;
  }

  public void clear() {
    synchronized (nonBlockingAnimations) {
      nonBlockingAnimations.clear();
    }
    synchronized (blockingAnimations) {
      blockingAnimations.clear();
    }
  }

  /**
   * Handler called when the boardgamer.yavalath.client.animation is deactivated (set to unblock for blocking animations,
   * killed for temporary animations)
   *
   * @author garysoed
   */
  public interface DeactivateHandler {

    abstract void onDeactivate();
  }
}
